{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "eb55ec85-c676-4e82-afca-9592a1a28bcb",
   "metadata": {},
   "source": [
    "- langgraph studio\n",
    "    - https://www.youtube.com/watch?v=pLPJoFvq4_M\n",
    "- https://github.com/langchain-ai/langgraph/blob/main/examples/reflexion/reflexion.ipynb\n",
    "- https://github.com/langchain-ai/langgraph/blob/main/examples/agent_executor/force-calling-a-tool-first.ipynb"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7c1155e5-b90a-4431-9112-b25b9c3a0892",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:30.218727Z",
     "iopub.status.busy": "2024-08-17T13:53:30.218218Z",
     "iopub.status.idle": "2024-08-17T13:53:31.596623Z",
     "shell.execute_reply": "2024-08-17T13:53:31.595421Z",
     "shell.execute_reply.started": "2024-08-17T13:53:30.218673Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "from typing import TypedDict, Annotated, List, Union, Sequence\n",
    "import operator\n",
    "import json\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "from langchain import hub\n",
    "from langchain.tools import BaseTool, StructuredTool, Tool, tool\n",
    "from langchain.tools.render import format_tool_to_openai_function\n",
    "from langchain_core.utils.function_calling import convert_to_openai_function\n",
    "from langchain_openai.chat_models import ChatOpenAI\n",
    "from langchain.agents import create_openai_functions_agent\n",
    "from langchain_core.utils.function_calling import convert_to_openai_function\n",
    "\n",
    "from langchain_core.agents import AgentAction, AgentFinish\n",
    "from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage, FunctionMessage\n",
    "\n",
    "from langgraph.graph import END, StateGraph, MessagesState\n",
    "from langgraph.prebuilt import ToolNode, ToolInvocation\n",
    "from langgraph.prebuilt.tool_executor import ToolExecutor\n",
    "\n",
    "os.environ['http_proxy'] = 'http://127.0.0.1:7890'\n",
    "os.environ['https_proxy'] = 'http://127.0.0.1:7890'\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "e8f7a5ae-18a2-4226-b9db-d6f7909a2482",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.597593Z",
     "iopub.status.busy": "2024-08-17T13:53:31.597301Z",
     "iopub.status.idle": "2024-08-17T13:53:31.607514Z",
     "shell.execute_reply": "2024-08-17T13:53:31.606826Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.597570Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2cdc889f-8c1f-4dec-a06a-764238a1c9e0",
   "metadata": {},
   "source": [
    "## core concepts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a3255780-8423-4205-b20a-858b4855b99f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.608558Z",
     "iopub.status.busy": "2024-08-17T13:53:31.608348Z",
     "iopub.status.idle": "2024-08-17T13:53:31.624596Z",
     "shell.execute_reply": "2024-08-17T13:53:31.623476Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.608540Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.display import Image"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81f3b684-f801-47b7-bed4-1afb50cbf58e",
   "metadata": {},
   "source": [
    "langchain => langgraph\n",
    "- chain: 更多是 linear chain，链式顺序执行；\n",
    "    - LCEL\n",
    "- graph：可以有分支/判断（是否到达 AgentFinish），有循环\n",
    "    - ReAct（Reasoning & Acting）其实本质上也不是 linear chain 了；\n",
    "\n",
    "Graph \n",
    "\n",
    "- nodes, edges\n",
    "    - 每个节点都是一个函数\n",
    "    - 所有节点的输入都是 state，输出会附加一些信息到新的 state 里\n",
    "- state 沿着 edge 从一个 node 到另一个 node 时，state 会发生变化（携带上一个 node 的执行输出信息）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "2f11534d-aee7-4065-9676-2fa3a3d01afb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.625367Z",
     "iopub.status.busy": "2024-08-17T13:53:31.625167Z",
     "iopub.status.idle": "2024-08-17T13:53:31.635656Z",
     "shell.execute_reply": "2024-08-17T13:53:31.634117Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.625349Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://miro.medium.com/v2/resize:fit:1400/format:webp/1*bkNjqXR1LjAVJQbNLpfYvA.png\" width=\"500\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='https://miro.medium.com/v2/resize:fit:1400/format:webp/1*bkNjqXR1LjAVJQbNLpfYvA.png', width=500)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "613565d5-68d7-462d-8688-d9413e6456d8",
   "metadata": {},
   "source": [
    "## examples 1: Agent Executor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "08341b08-4ace-4ce1-b3c4-7f1bfe2d0009",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.637053Z",
     "iopub.status.busy": "2024-08-17T13:53:31.636845Z",
     "iopub.status.idle": "2024-08-17T13:53:31.644030Z",
     "shell.execute_reply": "2024-08-17T13:53:31.642767Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.637034Z"
    }
   },
   "outputs": [],
   "source": [
    "os.environ[\"LANGCHAIN_PROJECT\"] = \"lg_01_agent_executor\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "37557636-0aba-4b9a-a365-5d761a92a2d4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.646389Z",
     "iopub.status.busy": "2024-08-17T13:53:31.646157Z",
     "iopub.status.idle": "2024-08-17T13:53:31.653834Z",
     "shell.execute_reply": "2024-08-17T13:53:31.652208Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.646369Z"
    }
   },
   "outputs": [],
   "source": [
    "class AgentState(TypedDict):\n",
    "   input: str\n",
    "   chat_history: list[BaseMessage]\n",
    "   agent_outcome: Union[AgentAction, AgentFinish, None]\n",
    "    # 不断追加和传递\n",
    "   intermediate_steps: Annotated[list[tuple[AgentAction, str]], operator.add]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdd50a57-2f37-4409-8cf3-83088f3e0b19",
   "metadata": {},
   "source": [
    "- TypedDict: 静态类型，可以约定 value 类型的字典"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a27b610d-bb48-40f6-a871-9a53c1025a90",
   "metadata": {},
   "source": [
    "### tools"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b380604d-8e75-4409-b7c9-a2d86184bbf9",
   "metadata": {},
   "source": [
    "Tools are interfaces that an agent can use to interact with the world. They combine a few things:\n",
    "\n",
    "- The name of the tool\n",
    "- A description of what the tool is\n",
    "- JSON schema of what the inputs to the tool are\n",
    "- The function to call"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "cd451e7c-327b-45f1-a338-d77334781e55",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.654736Z",
     "iopub.status.busy": "2024-08-17T13:53:31.654515Z",
     "iopub.status.idle": "2024-08-17T13:53:31.672179Z",
     "shell.execute_reply": "2024-08-17T13:53:31.670557Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.654716Z"
    }
   },
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "@tool(\"upper_case\", return_direct=True)\n",
    "def to_upper_case(input:str) -> str:\n",
    "  \"\"\"Returns the input as all upper case.\"\"\"\n",
    "  return input.upper()\n",
    "\n",
    "@tool(\"random_number\", return_direct=True)\n",
    "def random_number_maker(input:str) -> str:\n",
    "    \"\"\"Returns a random number between 0-100.\"\"\"\n",
    "    return random.randint(0, 100)\n",
    "\n",
    "tools = [to_upper_case,random_number_maker]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ec3c04e2-6dd6-45b4-979e-0085bc331516",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.673530Z",
     "iopub.status.busy": "2024-08-17T13:53:31.673302Z",
     "iopub.status.idle": "2024-08-17T13:53:31.694922Z",
     "shell.execute_reply": "2024-08-17T13:53:31.693805Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.673511Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random_number_maker.run('random')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "e29b3330-be73-40b2-ab3b-2331dbd14a20",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.695746Z",
     "iopub.status.busy": "2024-08-17T13:53:31.695533Z",
     "iopub.status.idle": "2024-08-17T13:53:31.703847Z",
     "shell.execute_reply": "2024-08-17T13:53:31.702754Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.695726Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'HELLO WORLD'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_upper_case.run('hello WORLD')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e757f87e-da41-42ca-8c4d-5b447b7c6f15",
   "metadata": {},
   "source": [
    "### Agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "966acec1-a9be-4382-b8d5-6b9032e6e0ad",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:31.704665Z",
     "iopub.status.busy": "2024-08-17T13:53:31.704445Z",
     "iopub.status.idle": "2024-08-17T13:53:33.150803Z",
     "shell.execute_reply": "2024-08-17T13:53:33.149195Z",
     "shell.execute_reply.started": "2024-08-17T13:53:31.704646Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatPromptTemplate(input_variables=['agent_scratchpad', 'input'], optional_variables=['chat_history'], input_types={'chat_history': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]], 'agent_scratchpad': typing.List[typing.Union[langchain_core.messages.ai.AIMessage, langchain_core.messages.human.HumanMessage, langchain_core.messages.chat.ChatMessage, langchain_core.messages.system.SystemMessage, langchain_core.messages.function.FunctionMessage, langchain_core.messages.tool.ToolMessage]]}, partial_variables={'chat_history': []}, metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'openai-functions-agent', 'lc_hub_commit_hash': 'a1655024b06afbd95d17449f21316291e0726f13dcfaf990cc0d18087ad689a5'}, messages=[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='You are a helpful assistant')), MessagesPlaceholder(variable_name='chat_history', optional=True), HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], template='{input}')), MessagesPlaceholder(variable_name='agent_scratchpad')])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# https://smith.langchain.com/hub/hwchase17/openai-functions-agent\n",
    "prompt = hub.pull('hwchase17/openai-functions-agent')\n",
    "llm = ChatOpenAI(model=\"gpt-4o\", streaming=True)\n",
    "prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "89771201-6c32-4ae3-b664-d7da9c1d39a7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:33.152061Z",
     "iopub.status.busy": "2024-08-17T13:53:33.151849Z",
     "iopub.status.idle": "2024-08-17T13:53:33.159355Z",
     "shell.execute_reply": "2024-08-17T13:53:33.158289Z",
     "shell.execute_reply.started": "2024-08-17T13:53:33.152042Z"
    }
   },
   "outputs": [],
   "source": [
    "agent = create_openai_functions_agent(llm, tools, prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "cd331493-adaf-4243-acec-5fe3ca72c133",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:33.160140Z",
     "iopub.status.busy": "2024-08-17T13:53:33.159938Z",
     "iopub.status.idle": "2024-08-17T13:53:33.179263Z",
     "shell.execute_reply": "2024-08-17T13:53:33.177815Z",
     "shell.execute_reply.started": "2024-08-17T13:53:33.160121Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain_core.runnables.base.RunnableSequence"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(agent)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "61e73f7d-43b4-4599-8f0b-2c1948a0ac04",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:33.180594Z",
     "iopub.status.busy": "2024-08-17T13:53:33.180391Z",
     "iopub.status.idle": "2024-08-17T13:53:34.651120Z",
     "shell.execute_reply": "2024-08-17T13:53:34.648882Z",
     "shell.execute_reply.started": "2024-08-17T13:53:33.180576Z"
    }
   },
   "outputs": [],
   "source": [
    "inputs = {\"input\": \"give me a random number and then write in words and make it upper case.\",\n",
    "          \"chat_history\": [],\n",
    "          \"intermediate_steps\":[]}\n",
    "\n",
    "agent_outcome = agent.invoke(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ac3ba32b-a322-4988-a722-16136a9eecaa",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:34.654281Z",
     "iopub.status.busy": "2024-08-17T13:53:34.653656Z",
     "iopub.status.idle": "2024-08-17T13:53:34.666319Z",
     "shell.execute_reply": "2024-08-17T13:53:34.664465Z",
     "shell.execute_reply.started": "2024-08-17T13:53:34.654230Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AgentActionMessageLog(tool='random_number', tool_input={'input': 'Generate a random number'}, log=\"\\nInvoking: `random_number` with `{'input': 'Generate a random number'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Generate a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-e2652fc2-03db-4c6c-9b26-310d80e0f06a-0')])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "agent_outcome"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6db04fd4-9a83-4a00-bc4a-feef8daebb38",
   "metadata": {},
   "source": [
    "### tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "e9185c9c-6482-4454-baed-bd4c1042e3c1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:34.668819Z",
     "iopub.status.busy": "2024-08-17T13:53:34.668293Z",
     "iopub.status.idle": "2024-08-17T13:53:34.684118Z",
     "shell.execute_reply": "2024-08-17T13:53:34.682502Z",
     "shell.execute_reply.started": "2024-08-17T13:53:34.668771Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1802818/3792225135.py:1: LangGraphDeprecationWarning: ToolExecutor is deprecated as of version 0.2.0 and will be removed in 0.3.0. Use langgraph.prebuilt.ToolNode instead.\n",
      "  tool_executor = ToolExecutor(tools)\n"
     ]
    }
   ],
   "source": [
    "tool_executor = ToolExecutor(tools)\n",
    "# tool_executor = ToolNode(tools)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "da6d4bb9-7328-4864-b14c-bc6b953c6f41",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:34.686727Z",
     "iopub.status.busy": "2024-08-17T13:53:34.686047Z",
     "iopub.status.idle": "2024-08-17T13:53:34.704441Z",
     "shell.execute_reply": "2024-08-17T13:53:34.702194Z",
     "shell.execute_reply.started": "2024-08-17T13:53:34.686679Z"
    }
   },
   "outputs": [],
   "source": [
    "def run_agent(state):\n",
    "    agent_outcome = agent.invoke(state)\n",
    "    # AgentState.agent_outcome: 当前输出，决策动作\n",
    "    return {\"agent_outcome\": agent_outcome}\n",
    "\n",
    "def execute_tools(state):\n",
    "    agent_action = state['agent_outcome']\n",
    "    output = tool_executor.invoke(agent_action)\n",
    "    print(f\"The agent action is {agent_action}\")\n",
    "    print(f\"The tool result is: {output}\")\n",
    "    # AgentState.intermediate_steps: 追加逻辑\n",
    "    return {\"intermediate_steps\": [(agent_action, str(output))]}\n",
    "\n",
    "def should_continue(state):\n",
    "    if isinstance(state['agent_outcome'], AgentFinish):\n",
    "        return \"end\"\n",
    "    return \"continue\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b78917e-8031-487b-a8f4-73c5cf87aaca",
   "metadata": {},
   "source": [
    "### graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "fc07813a-12a2-4108-9afe-6f0b35d79c07",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:34.709423Z",
     "iopub.status.busy": "2024-08-17T13:53:34.708881Z",
     "iopub.status.idle": "2024-08-17T13:53:34.730949Z",
     "shell.execute_reply": "2024-08-17T13:53:34.729166Z",
     "shell.execute_reply.started": "2024-08-17T13:53:34.709375Z"
    }
   },
   "outputs": [],
   "source": [
    "workflow = StateGraph(AgentState)\n",
    "\n",
    "workflow.add_node(\"agent\", run_agent)\n",
    "workflow.add_node(\"action\", execute_tools)\n",
    "\n",
    "workflow.set_entry_point(\"agent\")\n",
    "\n",
    "# 条件分支\n",
    "workflow.add_conditional_edges(\n",
    "    \"agent\",\n",
    "    should_continue,\n",
    "    {\n",
    "        \"continue\": \"action\",\n",
    "        \"end\": END\n",
    "    }\n",
    ")\n",
    "\n",
    "workflow.add_edge('action', 'agent')\n",
    "\n",
    "graph = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "91f8659d-a143-44d6-87c2-9b045c19a2e1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:34.733316Z",
     "iopub.status.busy": "2024-08-17T13:53:34.732797Z",
     "iopub.status.idle": "2024-08-17T13:53:36.799282Z",
     "shell.execute_reply": "2024-08-17T13:53:36.796972Z",
     "shell.execute_reply.started": "2024-08-17T13:53:34.733269Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "968a7e2d-3d6d-4d14-92b3-2177c539cfc5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:36.801802Z",
     "iopub.status.busy": "2024-08-17T13:53:36.801242Z",
     "iopub.status.idle": "2024-08-17T13:53:36.813609Z",
     "shell.execute_reply": "2024-08-17T13:53:36.812071Z",
     "shell.execute_reply.started": "2024-08-17T13:53:36.801752Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "({'agent': StateNodeSpec(runnable=agent(recurse=True), metadata=None, input=<class '__main__.AgentState'>, retry_policy=None),\n",
       "  'action': StateNodeSpec(runnable=action(recurse=True), metadata=None, input=<class '__main__.AgentState'>, retry_policy=None)},\n",
       " {('__start__', 'agent'), ('action', 'agent')})"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "workflow.nodes, workflow.edges"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "4918da09-5e3d-496f-a152-1576138c3814",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:36.815892Z",
     "iopub.status.busy": "2024-08-17T13:53:36.815386Z",
     "iopub.status.idle": "2024-08-17T13:53:36.834656Z",
     "shell.execute_reply": "2024-08-17T13:53:36.833097Z",
     "shell.execute_reply.started": "2024-08-17T13:53:36.815844Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': <langgraph.channels.last_value.LastValue at 0x77547bb3e0f0>,\n",
       " 'chat_history': <langgraph.channels.last_value.LastValue at 0x77547bb3dee0>,\n",
       " 'agent_outcome': <langgraph.channels.last_value.LastValue at 0x77547bb3dfd0>,\n",
       " 'intermediate_steps': <langgraph.channels.binop.BinaryOperatorAggregate at 0x775484081940>}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "workflow.channels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "1e11c70c-6d6d-400f-9ea5-c1788d634300",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:36.842571Z",
     "iopub.status.busy": "2024-08-17T13:53:36.842010Z",
     "iopub.status.idle": "2024-08-17T13:53:38.855838Z",
     "shell.execute_reply": "2024-08-17T13:53:38.853901Z",
     "shell.execute_reply.started": "2024-08-17T13:53:36.842522Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'agent_outcome': AgentActionMessageLog(tool='random_number', tool_input={'input': 'Give me a random number'}, log=\"\\nInvoking: `random_number` with `{'input': 'Give me a random number'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Give me a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-00c69965-ef08-4664-a251-2510db7afcf7-0')])}\n",
      "----\n",
      "The agent action is tool='random_number' tool_input={'input': 'Give me a random number'} log=\"\\nInvoking: `random_number` with `{'input': 'Give me a random number'}`\\n\\n\\n\" message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Give me a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-00c69965-ef08-4664-a251-2510db7afcf7-0')]\n",
      "The tool result is: 85\n",
      "{'intermediate_steps': [(AgentActionMessageLog(tool='random_number', tool_input={'input': 'Give me a random number'}, log=\"\\nInvoking: `random_number` with `{'input': 'Give me a random number'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Give me a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-00c69965-ef08-4664-a251-2510db7afcf7-0')]), '85')]}\n",
      "----\n",
      "{'agent_outcome': AgentFinish(return_values={'output': 'The random number is 85. In words, that is \"eighty-five.\"\\n\\nLet me convert that into upper case for you: EIGHTY-FIVE'}, log='The random number is 85. In words, that is \"eighty-five.\"\\n\\nLet me convert that into upper case for you: EIGHTY-FIVE')}\n",
      "----\n"
     ]
    }
   ],
   "source": [
    "inputs = {\"input\": \"give me a random number and then write in words and make it upper case.\", \n",
    "          \"chat_history\": []}\n",
    "for s in graph.stream(inputs):\n",
    "    print(list(s.values())[0])\n",
    "    print(\"----\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "397e0638-0ef1-4a51-b3c0-92516f373c28",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:19.999617Z",
     "iopub.status.busy": "2024-08-17T13:54:19.998993Z",
     "iopub.status.idle": "2024-08-17T13:54:23.795595Z",
     "shell.execute_reply": "2024-08-17T13:54:23.793637Z",
     "shell.execute_reply.started": "2024-08-17T13:54:19.999565Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The agent action is tool='random_number' tool_input={'input': 'Give me a random number'} log=\"\\nInvoking: `random_number` with `{'input': 'Give me a random number'}`\\n\\n\\n\" message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Give me a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-37b6ccdc-77c5-4af3-bdbf-874d0506622a-0')]\n",
      "The tool result is: 13\n",
      "The agent action is tool='upper_case' tool_input={'input': 'thirteen'} log=\"\\nInvoking: `upper_case` with `{'input': 'thirteen'}`\\n\\n\\n\" message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"thirteen\"}', 'name': 'upper_case'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-d159f4d6-7805-46f4-8029-41c9eaa0e699-0')]\n",
      "The tool result is: THIRTEEN\n"
     ]
    }
   ],
   "source": [
    "inputs = {\"input\": \"give me a random number and then write in words and make it upper case\", \"chat_history\": []}\n",
    "\n",
    "output = graph.invoke(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "c9881b70-1341-45e0-82c5-da57b0a66bc0",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:27.975426Z",
     "iopub.status.busy": "2024-08-17T13:54:27.974790Z",
     "iopub.status.idle": "2024-08-17T13:54:27.986820Z",
     "shell.execute_reply": "2024-08-17T13:54:27.984803Z",
     "shell.execute_reply.started": "2024-08-17T13:54:27.975373Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'input': 'give me a random number and then write in words and make it upper case',\n",
       " 'chat_history': [],\n",
       " 'agent_outcome': AgentFinish(return_values={'output': 'The random number is 13, which in words is: THIRTEEN'}, log='The random number is 13, which in words is: THIRTEEN'),\n",
       " 'intermediate_steps': [(AgentActionMessageLog(tool='random_number', tool_input={'input': 'Give me a random number'}, log=\"\\nInvoking: `random_number` with `{'input': 'Give me a random number'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Give me a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-37b6ccdc-77c5-4af3-bdbf-874d0506622a-0')]),\n",
       "   '13'),\n",
       "  (AgentActionMessageLog(tool='upper_case', tool_input={'input': 'thirteen'}, log=\"\\nInvoking: `upper_case` with `{'input': 'thirteen'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"thirteen\"}', 'name': 'upper_case'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-d159f4d6-7805-46f4-8029-41c9eaa0e699-0')]),\n",
       "   'THIRTEEN')]}"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "8b11a72c-a47d-4523-9add-d367c1c5e590",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:30.394131Z",
     "iopub.status.busy": "2024-08-17T13:54:30.393404Z",
     "iopub.status.idle": "2024-08-17T13:54:30.404773Z",
     "shell.execute_reply": "2024-08-17T13:54:30.402864Z",
     "shell.execute_reply.started": "2024-08-17T13:54:30.394075Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AgentFinish(return_values={'output': 'The random number is 13, which in words is: THIRTEEN'}, log='The random number is 13, which in words is: THIRTEEN')"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output.get('agent_outcome')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "c299fe0e-1e67-4ba8-a63b-35ee4a331c1e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:32.058001Z",
     "iopub.status.busy": "2024-08-17T13:54:32.057330Z",
     "iopub.status.idle": "2024-08-17T13:54:32.069862Z",
     "shell.execute_reply": "2024-08-17T13:54:32.068379Z",
     "shell.execute_reply.started": "2024-08-17T13:54:32.057949Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'The random number is 13, which in words is: THIRTEEN'"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output.get('agent_outcome').return_values['output']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "cf569ed5-0f2a-4492-a3dd-ca16a806e959",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:34.251056Z",
     "iopub.status.busy": "2024-08-17T13:54:34.250431Z",
     "iopub.status.idle": "2024-08-17T13:54:34.263482Z",
     "shell.execute_reply": "2024-08-17T13:54:34.261273Z",
     "shell.execute_reply.started": "2024-08-17T13:54:34.251005Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(AgentActionMessageLog(tool='random_number', tool_input={'input': 'Give me a random number'}, log=\"\\nInvoking: `random_number` with `{'input': 'Give me a random number'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"Give me a random number\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-37b6ccdc-77c5-4af3-bdbf-874d0506622a-0')]),\n",
       "  '13'),\n",
       " (AgentActionMessageLog(tool='upper_case', tool_input={'input': 'thirteen'}, log=\"\\nInvoking: `upper_case` with `{'input': 'thirteen'}`\\n\\n\\n\", message_log=[AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"thirteen\"}', 'name': 'upper_case'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-d159f4d6-7805-46f4-8029-41c9eaa0e699-0')]),\n",
       "  'THIRTEEN')]"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output['intermediate_steps']"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "82bdde06-9f24-4a32-97d2-6cda0eb5cfa1",
   "metadata": {},
   "source": [
    "## example 2: Chat Executor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "b1c8de74-86a4-4b92-bb01-d5136adff338",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.704307Z",
     "iopub.status.busy": "2024-08-17T13:53:40.703795Z",
     "iopub.status.idle": "2024-08-17T13:53:40.712175Z",
     "shell.execute_reply": "2024-08-17T13:53:40.710365Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.704259Z"
    }
   },
   "outputs": [],
   "source": [
    "os.environ[\"LANGCHAIN_PROJECT\"] = \"lg_02_chat_executor\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "fd3d34e7-fefa-4a55-9bcb-cf3da4ddcecd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.714885Z",
     "iopub.status.busy": "2024-08-17T13:53:40.714339Z",
     "iopub.status.idle": "2024-08-17T13:53:40.809681Z",
     "shell.execute_reply": "2024-08-17T13:53:40.808139Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.714836Z"
    }
   },
   "outputs": [],
   "source": [
    "llm = ChatOpenAI(model=\"gpt-4o\", streaming=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "f8f4868b-32d9-4926-a8e5-5a7e2bd6eae8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.810564Z",
     "iopub.status.busy": "2024-08-17T13:53:40.810357Z",
     "iopub.status.idle": "2024-08-17T13:53:40.817589Z",
     "shell.execute_reply": "2024-08-17T13:53:40.816511Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.810546Z"
    }
   },
   "outputs": [],
   "source": [
    "functions = [convert_to_openai_function(t) for t in tools]\n",
    "llm = llm.bind_functions(functions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "fd9ff1fb-958b-49f0-aadb-d4f04f04a7e9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.818829Z",
     "iopub.status.busy": "2024-08-17T13:53:40.818624Z",
     "iopub.status.idle": "2024-08-17T13:53:40.827845Z",
     "shell.execute_reply": "2024-08-17T13:53:40.826800Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.818810Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'name': 'upper_case',\n",
       "  'description': 'Returns the input as all upper case.',\n",
       "  'parameters': {'type': 'object',\n",
       "   'properties': {'input': {'type': 'string'}},\n",
       "   'required': ['input']}},\n",
       " {'name': 'random_number',\n",
       "  'description': 'Returns a random number between 0-100.',\n",
       "  'parameters': {'type': 'object',\n",
       "   'properties': {'input': {'type': 'string'}},\n",
       "   'required': ['input']}}]"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "functions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc735271-2a2c-4ed1-8099-7730f455fbe9",
   "metadata": {},
   "source": [
    "### agent state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "f8d5f05d-a592-4187-8244-639bed6cdb65",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.828678Z",
     "iopub.status.busy": "2024-08-17T13:53:40.828477Z",
     "iopub.status.idle": "2024-08-17T13:53:40.837269Z",
     "shell.execute_reply": "2024-08-17T13:53:40.835700Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.828661Z"
    }
   },
   "outputs": [],
   "source": [
    "class AgentState(TypedDict):\n",
    "    # 不断追加和传递\n",
    "    messages: Annotated[Sequence[BaseMessage], operator.add]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ce3aca0-08ac-4f37-bdc9-f57be9c38a4f",
   "metadata": {},
   "source": [
    "### nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "27d54c02-71c3-4698-bd03-d295abe5a125",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.838755Z",
     "iopub.status.busy": "2024-08-17T13:53:40.838526Z",
     "iopub.status.idle": "2024-08-17T13:53:40.847629Z",
     "shell.execute_reply": "2024-08-17T13:53:40.846020Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.838736Z"
    }
   },
   "outputs": [],
   "source": [
    "def should_continue(state):\n",
    "    messages = state['messages']\n",
    "    last_message = messages[-1]\n",
    "    if \"function_call\" not in last_message.additional_kwargs:\n",
    "        return \"end\"\n",
    "    return \"continue\"\n",
    "\n",
    "def call_model(state):\n",
    "    messages = state['messages']\n",
    "    response = llm.invoke(messages)\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "def call_tool(state):\n",
    "    messages = state['messages']\n",
    "    last_message = messages[-1]\n",
    "    action = ToolInvocation(\n",
    "        tool=last_message.additional_kwargs[\"function_call\"][\"name\"],\n",
    "        tool_input=json.loads(last_message.additional_kwargs[\"function_call\"][\"arguments\"]),\n",
    "    )\n",
    "    print(f\"The agent action is {action}\")\n",
    "    response = tool_executor.invoke(action)\n",
    "    print(f\"The tool result is: {response}\")\n",
    "    function_message = FunctionMessage(content=str(response), name=action.tool)\n",
    "    return {\"messages\": [function_message]}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "275c656f-3f10-4b40-b0d2-6c6fd5263a51",
   "metadata": {},
   "source": [
    "### graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "1bdf1b85-c547-4894-b693-caf3c3f6d591",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.849766Z",
     "iopub.status.busy": "2024-08-17T13:53:40.849333Z",
     "iopub.status.idle": "2024-08-17T13:53:40.861578Z",
     "shell.execute_reply": "2024-08-17T13:53:40.860175Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.849730Z"
    }
   },
   "outputs": [],
   "source": [
    "workflow = StateGraph(AgentState)\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", call_tool)\n",
    "workflow.set_entry_point(\"agent\")\n",
    "workflow.add_conditional_edges(\n",
    "    \"agent\",\n",
    "    should_continue,\n",
    "    {\n",
    "        \"continue\": \"action\",\n",
    "        \"end\": END\n",
    "    }\n",
    ")\n",
    "workflow.add_edge('action', 'agent')\n",
    "graph2 = workflow.compile()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "fcd110d8-50e1-4f48-adfe-9fb747032e45",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:40.863356Z",
     "iopub.status.busy": "2024-08-17T13:53:40.862971Z",
     "iopub.status.idle": "2024-08-17T13:53:41.795912Z",
     "shell.execute_reply": "2024-08-17T13:53:41.794001Z",
     "shell.execute_reply.started": "2024-08-17T13:53:40.863322Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "try:\n",
    "    display(Image(graph2.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "d343967b-8bc9-4456-99d9-2257fea8b434",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:44.762413Z",
     "iopub.status.busy": "2024-08-17T13:54:44.761767Z",
     "iopub.status.idle": "2024-08-17T13:54:44.771993Z",
     "shell.execute_reply": "2024-08-17T13:54:44.769988Z",
     "shell.execute_reply.started": "2024-08-17T13:54:44.762362Z"
    }
   },
   "outputs": [],
   "source": [
    "system_message = SystemMessage(content=\"you are a helpful assistant\")\n",
    "user_q1 = HumanMessage(content=\"give me a random number and then write in words and make it upper case\")\n",
    "inputs = {\"messages\": [system_message, user_q1]}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "d4ae6f6e-1f3e-4fc4-ab95-bf0f711ecc55",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:45.941167Z",
     "iopub.status.busy": "2024-08-17T13:54:45.940525Z",
     "iopub.status.idle": "2024-08-17T13:54:49.126605Z",
     "shell.execute_reply": "2024-08-17T13:54:49.124470Z",
     "shell.execute_reply.started": "2024-08-17T13:54:45.941116Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1802818/3059577378.py:16: LangGraphDeprecationWarning: ToolInvocation is deprecated as of version 0.2.0 and will be removed in 0.3.0. Use langgraph.prebuilt.ToolNode instead.\n",
      "  action = ToolInvocation(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The agent action is tool='random_number' tool_input={'input': 'random'}\n",
      "The tool result is: 17\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_1802818/3059577378.py:16: LangGraphDeprecationWarning: ToolInvocation is deprecated as of version 0.2.0 and will be removed in 0.3.0. Use langgraph.prebuilt.ToolNode instead.\n",
      "  action = ToolInvocation(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The agent action is tool='upper_case' tool_input={'input': 'seventeen'}\n",
      "The tool result is: SEVENTEEN\n"
     ]
    }
   ],
   "source": [
    "output = graph2.invoke(inputs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "1830ff82-1c66-42c7-b15a-54d7bb470878",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:53:45.148484Z",
     "iopub.status.busy": "2024-08-17T13:53:45.147959Z",
     "iopub.status.idle": "2024-08-17T13:53:45.155284Z",
     "shell.execute_reply": "2024-08-17T13:53:45.153401Z",
     "shell.execute_reply.started": "2024-08-17T13:53:45.148435Z"
    }
   },
   "outputs": [],
   "source": [
    "# type(output), isinstance(output, AgentState)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "cfc89ca9-9581-4638-94d9-979daab9fdd5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-08-17T13:54:51.477392Z",
     "iopub.status.busy": "2024-08-17T13:54:51.476756Z",
     "iopub.status.idle": "2024-08-17T13:54:51.489236Z",
     "shell.execute_reply": "2024-08-17T13:54:51.487365Z",
     "shell.execute_reply.started": "2024-08-17T13:54:51.477342Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'messages': [SystemMessage(content='you are a helpful assistant'),\n",
       "  HumanMessage(content='give me a random number and then write in words and make it upper case'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"random\"}', 'name': 'random_number'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-333b0a51-c40c-4e58-958a-b649b684f96e-0'),\n",
       "  FunctionMessage(content='17', name='random_number'),\n",
       "  AIMessage(content='', additional_kwargs={'function_call': {'arguments': '{\"input\":\"seventeen\"}', 'name': 'upper_case'}}, response_metadata={'finish_reason': 'function_call', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-6eae1c13-d9b0-4041-a39f-bd3c2b64e825-0'),\n",
       "  FunctionMessage(content='SEVENTEEN', name='upper_case'),\n",
       "  AIMessage(content='Your random number is **17**, which in words is **SEVENTEEN**.', response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-2024-05-13', 'system_fingerprint': 'fp_3aa7262c27'}, id='run-68762761-d744-473f-866a-da87f6a52371-0')]}"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "395363cb-6e0e-4566-be6c-8cec25751567",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "agent",
   "language": "python",
   "name": "agent"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
